home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / x11 / xconq.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  31.6 KB  |  1,410 lines  |  [TEXT/R*ch]

  1. /* The main program of the X11 interface to Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. #include "conq.h"
  11. extern void notify_instructions PARAMS ((void));
  12. extern void low_send PARAMS ((int id, char *buf));
  13. extern int low_receive PARAMS ((int *id, char *buf, int maxchars,
  14.                 int timeout));
  15. #include "cmdline.h"
  16. #include "xconq.h"
  17.  
  18. /* Local function declarations. */
  19.  
  20. static void quit_program PARAMS ((Widget wdgt, XEvent *event, String *params,
  21.                   Cardinal *num_params));
  22. static void run_game_proc PARAMS ((XtPointer client_data, XtIntervalId *id));
  23. static Boolean run_game_idle PARAMS ((XtPointer clientdata));
  24. static int handle_x_error PARAMS ((Display *dpy, XErrorEvent *evt));
  25. static int handle_xio_error PARAMS ((Display *dpy));
  26. static void handle_xt_error PARAMS ((String msg));
  27.  
  28. /* X-specific options, plus some NULL definitions so that they'll be
  29.    ignored by X parsing and get passed through to the generic Xconq
  30.    command-line parser. */
  31.  
  32. XrmOptionDescRec xoptions[] = {
  33.     { "-background",    "*background",    XrmoptionSepArg,    NULL },
  34.     { "-bg",        "*background",    XrmoptionSepArg,    NULL },
  35.     { "-display",    ".display",    XrmoptionSepArg,    NULL },
  36.     { "-f",        NULL,        XrmoptionSkipArg,    NULL },
  37.     { "-fg",        "*foreground",    XrmoptionSepArg,    NULL },
  38.     { "-fn",        "*font",    XrmoptionSepArg,    NULL },
  39.     { "-font",        "*font",    XrmoptionSepArg,    NULL },
  40.     { "-foreground",    "*foreground",    XrmoptionSepArg,    NULL },
  41.     { "-g",        NULL,        XrmoptionSkipArg,    NULL },
  42.     { "-geometry",    "*geometry",    XrmoptionSepArg,    NULL },
  43.     { "-n",        NULL,        XrmoptionNoArg,        NULL },
  44.     { "-name",        ".name",    XrmoptionSepArg,    NULL },
  45.     { "-xrm",        NULL,        XrmoptionResArg,    NULL }
  46. };
  47.  
  48. int xoptions_count;
  49.  
  50. /* Fallback resources come from an include file that is auto-generated
  51.    from Xconq.ad.  Note that this will only supply a basic b/w layout;
  52.    to do color, the Xconq-co.ad resources have to be loaded already. */
  53.  
  54. String fallback_resources[] = {
  55.  
  56. #include "xconqad.h"
  57.  
  58.   NULL
  59. };
  60.  
  61. int announced = FALSE;
  62.  
  63. char *announcemsg = NULL;
  64.  
  65. int nargs;
  66. Arg tmpargs[100];
  67.  
  68. XtAppContext thisapp;
  69.  
  70. Widget thistoplevel;
  71.  
  72. Widget choiceshell = NULL;
  73.  
  74. Widget scenarioshell = NULL;
  75.  
  76. XtActionsRec quit_actions_table[] = {
  77.     { "wm-quit", quit_program },
  78. };
  79.  
  80. static void
  81. quit_program(w, event, params, num_params)
  82. Widget w;
  83. XEvent *event;
  84. String *params;
  85. Cardinal *num_params;
  86. {
  87.     Side *side;
  88.     Map *map;
  89.  
  90.     /* The side with toplevel matching widget just quit. */
  91.     if (!find_side_and_map_via_a_toplevel(w, &side, &map))
  92.       return;
  93.  
  94.     if (w == side->ui->shell) {
  95.     /* Go into the standard quit command. */
  96.     do_quit(side, map);
  97.     /* (should reset prefixarg, or doesn't matter?) */
  98.     } else if (w == side->ui->help_shell) {
  99.     popdown_help(side);
  100.     }
  101. }
  102.  
  103. /* The main program. */
  104.  
  105. int
  106. main(argc, argv)
  107. int argc;
  108. char *argv[];
  109. {
  110.     init_library_path(NULL);
  111.     printf("\n              Welcome to X11 Xconq version %s\n\n",
  112.        version_string());
  113.     printf("%s", license_string());
  114.     print_any_news();
  115.     /* Fiddle with game module structures. */
  116.     clear_game_modules();
  117. #ifdef DEBUGGING
  118.     init_debug_to_stdout();
  119. #endif /* DEBUGGING */
  120.     /* Set up empty data structures. */
  121.     init_data_structures();
  122.     /* Do the usual Xt application setup. */
  123.     /* Note that this opens one display by default, which means that
  124.        later code should take note and not try to open this a second time.
  125.        Also, this will absorb all X-related arguments - anything remaining
  126.        is either a generic Xconq option or a mistake. */
  127.     xoptions_count = XtNumber(xoptions);
  128.     thistoplevel =
  129.       XtAppInitialize(&thisapp, PROGRAMCLASSNAME,
  130.               xoptions, XtNumber(xoptions), &argc, argv,
  131.               fallback_resources, (ArgList) NULL, 0);
  132.  
  133.     parse_command_line(argc, argv, general_options);
  134.  
  135.     if (option_popup_new_game_dialog) {
  136.     collect_possible_games();
  137.     popup_game_dialog();
  138.     }
  139.  
  140.     if (option_popup_new_game_dialog) {
  141.     check_player_displays();
  142.     } else {
  143.     load_all_modules();
  144.     check_game_validity();
  145.     parse_command_line(argc, argv, variant_options);
  146.     set_variants_from_options();
  147.     parse_command_line(argc, argv, player_options);
  148.     set_players_from_options();
  149.     make_trial_assignments();
  150.     check_player_displays();
  151.     /* Complain about anything that's left. */
  152.     parse_command_line(argc, argv, leftover_options);
  153.     /* (still need to merge some databases derived from display) */
  154.     }
  155.  
  156.     /* And, perform some once per application context initialization... */
  157.     XtAppAddActions(thisapp, quit_actions_table, XtNumber(quit_actions_table));
  158.     add_map_actions();
  159.     XtAppAddTimeOut(thisapp, 10, run_game_proc, NULL);
  160.     XtAppAddWorkProc(thisapp, run_game_idle, NULL);
  161.  
  162.     /* Do the time-consuming part of setup calculations. */
  163.     calculate_globals();
  164.     run_synth_methods();
  165.     final_init();
  166.     assign_players_to_sides();
  167.     print_instructions();
  168.     run_game(0);
  169.     /* Get the displays set up, but don't draw anything yet. */
  170.     init_all_displays();
  171.     /* Now bring up the init data on each display. */
  172.     init_redraws();
  173.     /* Set up the signal handlers. */
  174.     init_signal_handlers();
  175.     init_x_signal_handlers();
  176.     notify_instructions();
  177.     /* Go into the main play loop. */
  178.     XtAppMainLoop(thisapp);
  179.  
  180.     /* Humor the compiler. */
  181.     return 0;
  182. }
  183.  
  184. /* This is a timed call; it gets called periodically. */
  185.  
  186. static void
  187. run_game_proc(client_data, id)
  188. XtPointer client_data;
  189. XtIntervalId *id;
  190. {
  191.     int rslt;
  192.     unsigned long interval;
  193.  
  194.     /* Run the kernel itself. */
  195.     rslt = run_game(10);
  196.     /* Set up to call it again in a little while. */
  197.     /* If things are happening, call 40 times/sec, for responsiveness. */
  198.     interval = 25;
  199.     /* If nothing is happening right now, do at 4 times/sec. */
  200.     if (rslt == 0)
  201.       interval = 250;
  202.     XtAppAddTimeOut(thisapp, interval, run_game_proc, NULL);
  203. }
  204.  
  205. static Boolean
  206. run_game_idle(clientdata)
  207. XtPointer clientdata;
  208. {
  209.     Side *side;
  210.     Map *map;
  211.     Unit *unit;
  212.  
  213.     /* See if we should jump to another unit and make it current. */
  214.     for_all_sides(side) {
  215.     if (active_display(side)) {
  216.         for_all_maps(side, map) {
  217.         if (map->curtool == movetool) {
  218.             unit = autonext_unit_inbox(side, map->curunit, map->vp);
  219.             if (unit != NULL)
  220.               set_current_unit(side, map, unit);
  221.         }
  222.         }
  223.     }
  224.     }
  225.     /* Call us again! */
  226.     return False;
  227. }
  228.  
  229. /* The default (human) player is the current user on the current display. */
  230.  
  231. Player *
  232. add_default_player()
  233. {
  234.     Player *player = add_player();
  235.     
  236.     player->name = getenv("USER");
  237.     player->configname = getenv("XCONQ_CONFIG");
  238.     player->displayname = getenv("DISPLAY");
  239.     return player;
  240. }
  241.  
  242. /* An init error needs to have the command re-run. */
  243.  
  244. void
  245. low_init_error(str)
  246. char *str;
  247. {
  248.     fprintf(stderr, "Error: %s.\n", str);
  249.     fflush(stderr);
  250.     exit(1);
  251. }
  252.  
  253. /* A warning just gets displayed, no other action is taken. */
  254.  
  255. void
  256. low_init_warning(str)
  257. char *str;
  258. {
  259.     fprintf(stderr, "Warning: %s.\n", str);
  260.     fflush(stderr);
  261. }
  262.  
  263. void
  264. low_run_error(str)
  265. char *str;
  266. {
  267.     close_displays();
  268.     fprintf(stderr, "Error: %s.\n", str);
  269.     fflush(stderr);
  270.     fprintf(stderr, "Saving the game...");
  271.     write_entire_game_state(saved_game_filename());
  272.     fprintf(stderr, " done.\n");
  273.     exit(1);
  274. }
  275.  
  276. /* Runtime warnings are for when it's important to bug the players,
  277.    usually a problem with Xconq or a game design. */
  278.  
  279. void
  280. low_run_warning(str)
  281. char *str;
  282. {
  283.     notify_all("Warning: %s; continuing...", str);
  284. }
  285.  
  286. void
  287. print_form(form)
  288. Obj *form;
  289. {
  290.     print_form_and_value(stdout, form);
  291. }
  292.  
  293. void
  294. end_printing_forms()
  295. {
  296. }
  297.  
  298. void
  299. init_x_signal_handlers()
  300. {
  301.     XSetErrorHandler(handle_x_error);
  302.     XSetIOErrorHandler(handle_xio_error);
  303.     XtAppSetErrorHandler(thisapp, handle_xt_error);
  304. }
  305.  
  306. /* Handlers for X catastrophes attempt to do a save first. */
  307.  
  308. static int
  309. handle_x_error (dpy, evt)
  310. Display *dpy;
  311. XErrorEvent *evt;
  312. {
  313.     static int num_errors = 0;
  314.     char buf[BUFSIZE];
  315.  
  316.     XGetErrorText(dpy, evt->error_code, buf, BUFSIZE);
  317.     fprintf(stderr, "\nX error on display %s: %s\n", DisplayString(dpy), buf);
  318.     if (++num_errors >= 10) {
  319.         printf("\nX error: trying emergency save!\n");
  320.     /* Note that if the save fails too, we're totally hosed. */
  321.     /* (should use configurable name here) */
  322.         write_entire_game_state("ack!.xconq");
  323.     abort();
  324.     }
  325.     return 0;
  326. }
  327.  
  328. static int
  329. handle_xio_error (dpy)
  330. Display *dpy;
  331. {
  332.     fprintf(stderr, "\nX IO error on display %s: trying emergency save!\n",
  333.        DisplayString(dpy));
  334.     write_entire_game_state("ack!.xconq");
  335.     abort();
  336.     return 0;
  337. }
  338.  
  339. static void
  340. handle_xt_error(msg)
  341. String msg;
  342. {
  343.     fprintf(stderr, "Xt error: %s\n", msg);
  344.     /* Get a core dump to debug with. */
  345.     abort();
  346. }
  347.  
  348. /* Reading is usually pretty fast, so don't do anything special here. */
  349.  
  350. void
  351. announce_read_progress()
  352. {
  353. }
  354.  
  355. /* Announce the start of a time-consuming computation. */
  356.  
  357. void
  358. announce_lengthy_process(msg)
  359. char *msg;
  360. {
  361.     n_seconds_elapsed(0);
  362.     announcemsg = copy_string(msg);
  363.     if (announcemsg) {
  364.     printf("%s;", announcemsg);
  365.     announcemsg = NULL;
  366.     fflush(stdout);
  367.     announced = TRUE;
  368.     }
  369. }
  370.  
  371. /* Announce the making of progress on the computation. */
  372.  
  373. void
  374. announce_progress(percentdone)
  375. int percentdone;
  376. {
  377.     if (n_seconds_elapsed(2)) {
  378.     printf(" %d%%,", percentdone);
  379.     fflush(stdout);
  380.     announced = TRUE;
  381.     }
  382. }
  383.  
  384. /* Announce the end of the time-consuming computation. */
  385.  
  386. void
  387. finish_lengthy_process()
  388. {
  389.     if (announced) {
  390.     printf(" done.\n");
  391.     announced = FALSE;
  392.     }
  393. }
  394.  
  395. /* All update_xxx_display callbacks are here. */
  396.  
  397. /* Draw an individual detailed hex, as a row of one, on all maps. */
  398.  
  399. void
  400. update_cell_display(side, x, y, rightnow)
  401. Side *side;
  402. int x, y, rightnow;
  403. {
  404.     Map *map;
  405.  
  406.     if (active_display(side)) {
  407.     for_all_maps(side, map) {
  408.         if (rightnow == 36)
  409.           continue;
  410.         draw_row(side, map, x, y, 1, TRUE);
  411.         if (map->curunit && map->curunit->x == x && map->curunit->y == y) {
  412.         draw_current(side, map);
  413.         }
  414.     }
  415.     if (rightnow)
  416.       flush_output(side);
  417.     }
  418. }
  419.  
  420. /* The kernel calls this to update info about the given side. */
  421.  
  422. void
  423. update_side_display(side, side2, rightnow)
  424. Side *side, *side2;
  425. int rightnow;
  426. {
  427.     Map *map;
  428.  
  429.     if (active_display(side)) {
  430.     for_all_maps(side, map) {
  431.         draw_side_info(side, map, side2);
  432.         /* (is this handled elsewhere also?) */
  433.         if (endofgame) {
  434.         if (!all_see_all)
  435.           side->may_set_see_all = TRUE;
  436.         update_controls(side, map);
  437.         }
  438.     }
  439.     if (rightnow)
  440.       flush_output(side);
  441.     }
  442. }
  443.  
  444. /* The kernel calls this to update info about the given unit. */
  445.  
  446. void
  447. update_unit_display(side, unit, rightnow)
  448. Side *side;
  449. Unit *unit;
  450. int rightnow;
  451. {
  452.     Map *map;
  453.  
  454.     if (active_display(side) && unit != NULL) {
  455.     if (inside_area(unit->x, unit->y)) {
  456.         update_cell_display(side, unit->x, unit->y, rightnow);
  457.     }
  458.     /* Redraw any/all info about unit and its side. */
  459.     for_all_maps(side, map) {
  460.         draw_map_info(side, map);
  461.         draw_side_info(side, map, unit->side);
  462.         update_unit_type_list(side, map, unit->type);
  463.     }
  464.     if (rightnow)
  465.       flush_output(side);
  466.     }
  467. }
  468.  
  469. void
  470. update_unit_acp_display(side, unit, rightnow)
  471. Side *side;
  472. Unit *unit;
  473. int rightnow;
  474. {
  475.     if (active_display(side)) {
  476.     }
  477. }
  478.  
  479. void
  480. update_action_result_display(side, unit, rslt, rightnow)
  481. Side *side;
  482. Unit *unit;
  483. int rslt, rightnow;
  484. {
  485.     Action *action;
  486.     Unit *unit2;
  487.     char *unit2handle = NULL;
  488.  
  489.     if (active_display(side)) {
  490.     action = &(unit->act->nextaction);
  491.     switch (rslt) {
  492.       case A_ANY_DONE:
  493.         /* (anything worthwhile to show?) */
  494.         break;
  495.       case A_ANY_TOO_FAR:
  496.         if (action->type == ACTION_FIRE_AT) {
  497.           unit2 = find_unit(action->args[0]);
  498.           if (unit2) {
  499.         unit2handle = unit_handle(side, unit2);
  500.         if (unit2handle)
  501.           unit2handle = copy_string(unit2handle);
  502.             notify(side, "Distance to %s (%d) is out of range (%d) of %s.",
  503.             unit2handle ? unit2handle : "target",
  504.             distance(unit->x, unit->y, unit2->x, unit2->y),
  505.             u_range(unit->type),
  506.             unit_handle(side, unit));
  507.         if (unit2handle)
  508.             free(unit2handle);
  509.           } else {
  510.         notify(side, "That is out of range of your %s.",
  511.             unit_handle(side, unit));
  512.           }
  513.         }
  514.         break;
  515.       default:
  516.         break;
  517.     }
  518.     if (Debug) {
  519.         notify(side, "%s %s %s!", unit_desig(unit),
  520.            action_desig(action), hevtdefns[rslt].name);
  521.     }
  522.     }
  523. }
  524.  
  525. /* The kernel calls this to update the global game state. */
  526.  
  527. void
  528. update_turn_display(side, rightnow)
  529. Side *side;
  530. int rightnow;
  531. {
  532.     int u;
  533.     Map *map;
  534.  
  535.     if (active_display(side)) {
  536.     for_all_maps(side, map) {
  537.         draw_game_state(side, map);
  538.         draw_view_in_panner(side, map);
  539.         for_all_unit_types(u) {
  540.         update_unit_type_list(side, map, u);
  541.         }
  542.     }
  543.     if (rightnow)
  544.       flush_output(side);
  545.     }
  546. }
  547.  
  548. void
  549. update_action_display(side, rightnow)
  550. Side *side;
  551. int rightnow;
  552. {
  553.     if (active_display(side)) {
  554.     /* show state of actions */
  555.     if (rightnow)
  556.       flush_output(side);
  557.     }
  558. }
  559.  
  560. void
  561. update_event_display(side, hevt, rightnow)
  562. Side *side;
  563. HistEvent *hevt;
  564. int rightnow;
  565. {
  566.     Unit *unit;
  567.     Side *side2;
  568.     Map *map;
  569.  
  570.     if (active_display(side)) {
  571.     switch (hevt->type) {
  572.       case H_SIDE_LOST:
  573.         if (hevt->data[0] == side_number(side)) {
  574.         notify(side, "You lost!");
  575.         } else {
  576.         notify(side, "%s lost!", side_desig(side_n(hevt->data[0])));
  577.         }
  578.         break;
  579.       case H_SIDE_WON:
  580.         if (hevt->data[0] == side_number(side)) {
  581.         notify(side, "You won!");
  582.         } else {
  583.         notify(side, "%s won!", side_desig(side_n(hevt->data[0])));
  584.         }
  585.         break;
  586.       case H_GAME_ENDED:
  587.         notify(side, "The game is over!");
  588.         /* Enable us to see everything on the map accurately. */
  589.         if (!all_see_all)
  590.           side->may_set_see_all = TRUE;
  591.         for_all_maps(side, map) {
  592.         update_controls(side, map);
  593.         }
  594.         break;
  595.       case H_UNIT_COMPLETED:
  596.         side2 = side_n(hevt->data[0]);
  597.         unit = find_unit(hevt->data[1]);
  598.         if (unit != NULL) {
  599.         if (side2 == side) {
  600.             notify(side, "You completed %s.",
  601.                unit_handle(side, unit));
  602.         } else {
  603.             notify(side, "%s completed %s.",
  604.                side_desig(side2), unit_handle(side2, unit));
  605.         }
  606.         }
  607.         break;
  608.       case H_UNIT_CREATED:
  609.         side2 = side_n(hevt->data[0]);
  610.         unit = find_unit(hevt->data[1]);
  611.         if (unit != NULL) {
  612.         if (side2 == side) {
  613.             notify(side, "You created %s.",
  614.                unit_handle(side, unit));
  615.         } else {
  616.             notify(side, "%s created %s.",
  617.                side_desig(side2), unit_handle(side2, unit));
  618.         }
  619.         }
  620.         break;
  621.       default:
  622.         /* No special display desired. */
  623.         break;
  624.     }
  625.     if (rightnow)
  626.       flush_output(side);
  627.     }
  628. }
  629.  
  630. struct fire_state {
  631.     Side *side;
  632.     Map *map;
  633.     int sx1, sy1, sw1, sh1, sx2, sy2, sw2, sh2;
  634.     int step;
  635. } firestates[10];
  636.  
  637. static void
  638. animate_fire_proc(client_data, id)
  639. XtPointer client_data;
  640. XtIntervalId *id;
  641. {
  642.     int which = (int) client_data;
  643.     int i, sx1, sy1, sw1, sh1, sx2, sy2, sw2, sh2, dx, dy, xx, yy;
  644.     Side *side;
  645.     Map *map;
  646.  
  647.     side = firestates[which].side;
  648.     map = firestates[which].map;
  649.     sx1 = firestates[which].sx1;  sy1 = firestates[which].sy1;
  650.     sw1 = firestates[which].sw1;  sh1 = firestates[which].sh1;
  651.     sx2 = firestates[which].sx2;  sy2 = firestates[which].sy2;
  652.     sw2 = firestates[which].sw2;  sh2 = firestates[which].sh2;
  653.     XSetFunction(side->ui->dpy, side->ui->gc, GXinvert);
  654.     XSetLineAttributes(side->ui->dpy, side->ui->gc,
  655.                2, LineSolid, CapButt, JoinMiter); 
  656.     compute_fire_line_segment(sx1 + sw1 / 2, sy1 + sh1 / 2,
  657.                   sx2 + sw2 / 2, sy2 + sh2 / 2,
  658.                   firestates[which].step, 4, &xx, &yy, &dx, &dy);
  659.     /* Draw one segment of a line between the units. */
  660.     if (dx > 0 || dy > 0) {
  661.     XDrawLine(side->ui->dpy, map->viewwin, side->ui->gc,
  662.           xx, yy, xx + dx, yy + dy);
  663.     flush_output(side);
  664.     }
  665.     XSetFunction(side->ui->dpy, side->ui->gc, GXcopy);
  666.     XSetLineAttributes(side->ui->dpy, side->ui->gc,
  667.                1, LineSolid, CapButt, JoinMiter); 
  668.     ++(firestates[which].step);
  669.     if (firestates[which].step < 24) {
  670.     XtAppAddTimeOut(thisapp, 150, animate_fire_proc, NULL);
  671.     }
  672. }
  673.  
  674. void
  675. update_fire_at_display(side, unit, unit2, m, rightnow)
  676. Side *side;
  677. Unit *unit, *unit2;
  678. int m, rightnow;
  679. {
  680.     int i, sx1, sy1, sw1, sh1, sx2, sy2, sw2, sh2, dx, dy, xx, yy;
  681.     char *xxx, extrabuf[BUFSIZE];
  682.     Map *map;
  683.     
  684.     if (active_display(side)) {
  685.     for_all_maps(side, map) {
  686.         x_xform_unit(side, map, unit, &sx1, &sy1, &sw1, &sh1);
  687.         x_xform_unit(side, map, unit2, &sx2, &sy2, &sw2, &sh2);
  688.         firestates[0].side = side;
  689.         firestates[0].map = map;
  690.         firestates[0].sx1 = sx1;  firestates[0].sy1 = sy1;
  691.         firestates[0].sw1 = sw1;  firestates[0].sh1 = sh1;
  692.         firestates[0].sx2 = sx2;  firestates[0].sy2 = sy2;
  693.         firestates[0].sw2 = sw2;  firestates[0].sh2 = sh2;
  694.         firestates[0].step = 0;
  695.         XtAppAddTimeOut(thisapp, 10, animate_fire_proc, NULL);
  696.     }
  697.     xxx = unit_handle(side, unit);
  698.     strcpy(extrabuf, xxx);
  699.     notify(side, "%s fired at %s!", extrabuf, unit_handle(side, unit2));
  700.     }
  701. }
  702.  
  703. #if 0
  704.     XSetFunction(side->ui->dpy, side->ui->gc, GXinvert);
  705.     XSetLineAttributes(side->ui->dpy, side->ui->gc, 2, LineSolid, CapButt, JoinMiter); 
  706.     i = 0;
  707.     while (i < 12) {  /* should be a timed loop */
  708.         for_all_maps(side, map) {
  709.         x_xform_unit(side, map, unit, &sx1, &sy1, &sw1, &sh1);
  710.         x_xform_unit(side, map, unit2, &sx2, &sy2, &sw2, &sh2);
  711.         compute_fire_line_segment(sx1 + sw1 / 2, sy1 + sh1 / 2,
  712.                       sx2 + sw2 / 2, sy2 + sh2 / 2,
  713.                       i, 4, &xx, &yy, &dx, &dy);
  714.         /* Draw one segment of a line between the units. */
  715.         if (dx > 0 || dy > 0) {
  716.             XDrawLine(side->ui->dpy, map->viewwin, side->ui->gc,
  717.                   xx, yy, xx + dx, yy + dy);
  718.             flush_output(side);
  719.         }
  720.         }
  721.         ++i;
  722.     }
  723.     /* (should clean up after drawing) */
  724.     xxx = unit_handle(side, unit);
  725.     strcpy(extrabuf, xxx);
  726.     notify(side, "%s fired at %s!",
  727.            extrabuf, unit_handle(side, unit2));
  728.     XSetFunction(side->ui->dpy, side->ui->gc, GXcopy);
  729.     XSetLineAttributes(side->ui->dpy, side->ui->gc, 1, LineSolid, CapButt, JoinMiter); 
  730.     }
  731. }
  732. #endif
  733.  
  734. /* This is for animation of fire-into actions. */
  735.  
  736. void
  737. update_fire_into_display(side, unit, x, y, z, m, rightnow)
  738. Side *side;
  739. Unit *unit;
  740. int x, y, z, m, rightnow;
  741. {
  742.     int i, sx1, sy1, sw1, sh1, sx2, sy2, sw2, sh2, dx, dy, xx, yy;
  743.     Map *map;
  744.     
  745.     if (active_display(side)) {
  746.     XSetFunction(side->ui->dpy, side->ui->gc, GXinvert);
  747.     XSetLineAttributes(side->ui->dpy, side->ui->gc, 2, LineSolid, CapButt, JoinMiter); 
  748.     i = 0;
  749.     while (i < 12) {  /* should be a timed loop */
  750.         for_all_maps(side, map) {
  751.         x_xform_unit(side, map, unit, &sx1, &sy1, &sw1, &sh1);
  752.         xform(side, map, x, y, &sx2, &sy2);
  753.         sw2 = map->vp->hw;  sh2 = map->vp->hh;
  754.         compute_fire_line_segment(sx1 + sw1 / 2, sy1 + sh1 / 2,
  755.                       sx2 + sw2 / 2, sy2 + sh2 / 2,
  756.                       i, 4, &xx, &yy, &dx, &dy);
  757.         if (dx > 0 || dy > 0) {
  758.             XDrawLine(side->ui->dpy, map->viewwin, side->ui->gc,
  759.                   xx, yy, xx + dx, yy + dy);
  760.             flush_output(side);
  761.         }
  762.         }
  763.         ++i;
  764.     }
  765.     /* (should clean up after drawing) */
  766.     notify(side, "%s fired into %d,%d!",
  767.            unit_handle(side, unit), x, y);
  768.     XSetFunction(side->ui->dpy, side->ui->gc, GXcopy);
  769.     XSetLineAttributes(side->ui->dpy, side->ui->gc, 1, LineSolid, CapButt, JoinMiter); 
  770.     }
  771. }
  772.  
  773. /* Updates to clock need to be sure that display changes immediately. */
  774.  
  775. void
  776. update_clock_display(side, rightnow)
  777. Side *side;
  778. int rightnow;
  779. {
  780.     Map *map;
  781.  
  782.     if (active_display(side)) {
  783.     for_all_maps(side, map) {
  784.         draw_game_clocks(side, map);
  785.     }
  786.     if (rightnow)
  787.       flush_output(side);
  788.     }
  789. }
  790.  
  791. void
  792. update_message_display(side, sender, str, rightnow)
  793. Side *side, *sender;
  794. char *str;
  795. int rightnow;
  796. {
  797.     if (active_display(side)) {
  798.     notify(side, "From %s: \"%s\"",
  799.            (sender != NULL ? short_side_title(sender) : "<anon>"), str);
  800.     if (rightnow)
  801.       flush_output(side);
  802.     }
  803. }
  804.  
  805. void
  806. update_all_progress_displays(str, s)
  807. char *str;
  808. int s;
  809. {
  810. }
  811.  
  812. void
  813. action_point(side, x, y)
  814. Side *side;
  815. int x, y;
  816. {
  817.     Map *map;
  818.  
  819.     if (!active_display(side))
  820.       return;
  821.     if (!inside_area(x, y))
  822.       return;
  823.  
  824.     for_all_maps(side, map) {
  825.     if (map->follow_action && !in_middle(side, map, x, y)) {
  826.         put_on_screen(side, map, x, y);
  827.     }
  828.     }    
  829. }
  830.  
  831. void
  832. flush_display_buffers(side)
  833. Side *side;
  834. {
  835.     if (active_display(side)) {
  836.     flush_output(side);
  837.     }
  838. }
  839.  
  840. void
  841. update_everything()
  842. {
  843.     Side *side;
  844.  
  845.     for_all_sides(side) {
  846.     if (active_display(side)) {
  847.         /* (update all maps?) */
  848.     }
  849.     }
  850. }
  851.  
  852. /* Close all displays that are still open. */
  853.  
  854. void
  855. close_displays()
  856. {
  857.     Side *side;
  858.  
  859.     for_all_sides(side) {
  860.     if (active_display(side))
  861.       close_display(side);
  862.     }
  863. }
  864.  
  865. /* This routine should be called before any sort of normal exit. */
  866.  
  867. void
  868. exit_xconq(side)
  869. Side *side;
  870. {
  871.     /* (should this really happen first?) */
  872.     close_displays();
  873.     /* maybe record in scorefile */
  874.     printf("%s\n", exit_commentary(side));
  875.     exit(0);
  876. }
  877.  
  878. int
  879. #ifdef ANSI_PROTOTYPES
  880. schedule_movie(Side *side, enum movie_type movie, ...)
  881. #else
  882. schedule_movie(side, movie, a1, a2, a3, a4, a5)
  883. Side *side;
  884. enum movie_type movie;
  885. int a1, a2, a3, a4, a5;
  886. #endif
  887. {
  888.     int i;
  889.  
  890.     if (!active_display(side))
  891.       return FALSE;
  892.     if (side->ui->numscheduled >= 10)
  893.       return FALSE;
  894.     memset(&(side->ui->movies[side->ui->numscheduled]), 0, sizeof(struct a_movie));
  895.     side->ui->movies[side->ui->numscheduled].type = movie;
  896. #ifdef ANSI_PROTOTYPES
  897.     {
  898.     va_list ap;
  899.  
  900.     va_start(ap, movie);
  901.     for (i = 0; i < 5; ++i)
  902.       side->ui->movies[side->ui->numscheduled].args[i] = va_arg(ap, int);
  903.     va_end(ap);
  904.     }
  905. #else
  906.     side->ui->movies[side->ui->numscheduled].args[0] = a1;
  907.     side->ui->movies[side->ui->numscheduled].args[1] = a2;
  908.     side->ui->movies[side->ui->numscheduled].args[2] = a3;
  909.     side->ui->movies[side->ui->numscheduled].args[3] = a4;
  910.     side->ui->movies[side->ui->numscheduled].args[4] = a5;
  911. #endif
  912.     ++side->ui->numscheduled;
  913.     return TRUE;
  914. }
  915.  
  916. void
  917. play_movies(sidemask)
  918. SideMask sidemask;
  919. {
  920.     int j, unitid, sx, sy, sw, sh;
  921.     Map *map;
  922.     Unit *unit;
  923.     Side *side;
  924.  
  925.     for_all_sides(side) {
  926.     if (side_in_set(side, sidemask) && active_display(side)) {
  927.         for (j = 0; j < side->ui->numscheduled; ++j) {
  928.         switch (side->ui->movies[j].type) {
  929.           case movie_null:
  930.             break;
  931.           case movie_miss:
  932.             unitid = side->ui->movies[j].args[0];
  933.             unit = find_unit(unitid);
  934.             if (unit == NULL || !in_area(unit->x, unit->y))
  935.               continue;
  936.             for_all_maps(side, map) {
  937.             x_xform_unit(side, map, unit, &sx, &sy, &sw, &sh);
  938.             draw_blast_image(side, map, sx, sy, sw, sh, 0);
  939.             }
  940.             break;
  941.           case movie_hit:
  942.             unitid = side->ui->movies[j].args[0];
  943.             unit = find_unit(unitid);
  944.             if (unit == NULL || !in_area(unit->x, unit->y))
  945.               continue;
  946.             for_all_maps(side, map) {
  947.             x_xform_unit(side, map, unit, &sx, &sy, &sw, &sh);
  948.             draw_blast_image(side, map, sx, sy, sw, sh, 1);
  949.             }
  950.             break;
  951.           case movie_death:
  952.             unitid = side->ui->movies[j].args[0];
  953.             unit = find_unit(unitid);
  954.             if (unit == NULL || !in_area(unit->x, unit->y))
  955.               continue;
  956.             for_all_maps(side, map) {
  957.             x_xform_unit(side, map, unit, &sx, &sy, &sw, &sh);
  958.             draw_blast_image(side, map, sx, sy, sw, sh, 2);
  959.             }
  960.             break;
  961.           case movie_nuke:
  962.             break;
  963.           default:
  964.             break;
  965.         }
  966.         }
  967.         side->ui->numscheduled = 0;
  968.     }
  969.     }
  970. }
  971.  
  972. void
  973. ui_save_state(side)
  974. Side *side;
  975. {
  976.   Obj *state = lispnil;
  977.  
  978.   side->uidata = replace_at_key(side->uidata, "x11", state);
  979. }
  980.  
  981. /* Get rid of extra input events, generally because the situation has
  982.    changed drastically and they are no longer of interest.  This routine
  983.    has relatively few valid uses. */
  984.  
  985. void
  986. flush_input(side)
  987. Side *side;
  988. {
  989.     DGprintf("doing an input flush\n");
  990.     if (active_display(side)) {
  991.     XSync(side->ui->dpy, TRUE);  
  992.     }
  993. }
  994.  
  995. /* Build a X-compatible widget name, by replacing non-alphanumerics with
  996.    underscores. */
  997.  
  998. void
  999. build_name(name, first, second)
  1000. char *name, *first, *second;
  1001. {
  1002.     char *ch;
  1003.  
  1004.     strcpy(name, first);
  1005.     strcat(name, second);
  1006.     for (ch = name; *ch != '\0'; ++ch) {
  1007.     if (!isalnum(*ch))
  1008.       *ch = '_';
  1009.     }
  1010. }
  1011.  
  1012. #ifdef DEBUGGING
  1013.  
  1014. /* Set all the window backgrounds, borders, GCs, etc, usually to reflect
  1015.    inversion of foreground and background. */
  1016.  
  1017. void
  1018. reset_color_state(side)
  1019. Side *side;
  1020. {
  1021.     XGCValues values;
  1022.     unsigned int gcmask = GCForeground | GCBackground;
  1023.  
  1024.     /* Set the GCs. */
  1025.     values.foreground = side->ui->fgcolor;
  1026.     values.background = side->ui->bgcolor;
  1027.     XChangeGC(side->ui->dpy, side->ui->gc, gcmask, &values);
  1028.     XChangeGC(side->ui->dpy, side->ui->textgc, gcmask, &values);
  1029.     XChangeGC(side->ui->dpy, side->ui->ltextgc, gcmask, &values);
  1030.     XChangeGC(side->ui->dpy, side->ui->unitgc, gcmask, &values);
  1031. }
  1032.  
  1033. void
  1034. reset_window_colors(side, win)
  1035. Side *side;
  1036. Window win;
  1037. {
  1038.     XSetWindowBackground(side->ui->dpy, win, side->ui->bgcolor);
  1039.     XSetWindowBorder(side->ui->dpy, win, side->ui->fgcolor);
  1040. }
  1041.  
  1042. #endif /* DEBUGGING */
  1043.  
  1044. /* Completely redo all windows, making no assumptions about appearance.
  1045.    This is a last-gasp measure, most redrawing should be restricted
  1046.    to only the directly affected windows.  Also this shouldn't be
  1047.    done without the user's permission, since it will blow away impending
  1048.    input. */
  1049.  
  1050. void
  1051. redraw(side)
  1052. Side *side;
  1053. {
  1054.     draw_all_maps(side);
  1055.     /* (should do popups also?) */
  1056.     flush_output(side);
  1057.     flush_input(side);
  1058. }
  1059.  
  1060. /* Trivial abstraction - sometimes other routines want to ensure all output
  1061.    so far is actually on the screen and not being buffered up somewhere. */
  1062.  
  1063. void
  1064. flush_output(side) 
  1065. Side *side; 
  1066. {  
  1067.     XFlush(side->ui->dpy);  
  1068. }
  1069.  
  1070. /* Beep the beeper! */
  1071.  
  1072. void
  1073. beep(side)
  1074. Side *side;
  1075. {
  1076.     XBell(side->ui->dpy, side->ui->screen);
  1077. }
  1078.  
  1079. void
  1080. low_notify(side, str)
  1081. Side *side;
  1082. char *str;
  1083. {
  1084.     Map *map;
  1085.  
  1086.     if (!active_display(side))
  1087.       return;
  1088.     /* Paste into a subwindow of each map window. */
  1089.     for_all_maps(side, map) {
  1090.     textw_printf(map->history, "%s\n", str);
  1091.     }
  1092.     DGprintf("To %s: %s\n", side_desig(side), str);
  1093. }
  1094.  
  1095. /* General text drawer. */
  1096.  
  1097. void
  1098. draw_text(side, win, x, y, str, color)
  1099. Side *side;
  1100. Window win;
  1101. int x, y, color;
  1102. char *str;
  1103. {
  1104.     XFontStruct *font = side->ui->textfont;
  1105.     GC gc = side->ui->textgc;
  1106.     Display *dpy = side->ui->dpy;
  1107.  
  1108.     y += font->max_bounds.ascent;
  1109.     XSetForeground(dpy, gc, color);
  1110.     /* If "color" is bg, assume we want reverse video. */
  1111.     XSetBackground(dpy, gc, (color == side->ui->bgcolor ? side->ui->fgcolor : side->ui->bgcolor));
  1112.     XDrawImageString(dpy, win, gc, x, y, str, strlen(str));
  1113. #ifdef STUPIDFLUSH
  1114.     XFlush(dpy);
  1115. #endif STUPIDFLUSH
  1116. }
  1117.  
  1118. /* Frequently-called routine to draw text in the foreground color. */
  1119.  
  1120. void
  1121. draw_fg_text(side, win, x, y, str)
  1122. Side *side;
  1123. Window win;
  1124. int x, y;
  1125. char *str;
  1126. {
  1127.     draw_text(side, win, x, y, str, side->ui->fgcolor);
  1128. }
  1129.  
  1130. XawTextPosition
  1131. widget_text_length(w)
  1132. Widget w;
  1133. {
  1134.     return XawTextSourceScan (XawTextGetSource (w),
  1135.                   (XawTextPosition) 0,
  1136.                    XawstAll, XawsdRight, 1, TRUE);
  1137. }
  1138.  
  1139. #if 0
  1140. void
  1141. move_caret_to_start(w)
  1142. Widget w;
  1143. {
  1144.     XawTextSetInsertionPoint(w, (XawTextPosition) 0);
  1145. }
  1146. #endif
  1147.  
  1148. void
  1149. move_caret_to_end(w)
  1150. Widget w;
  1151. {
  1152.     XawTextPosition pos;
  1153.  
  1154.     pos = widget_text_length(w);
  1155.     XawTextSetInsertionPoint(w, pos);
  1156. }
  1157.  
  1158. static int count_lines PARAMS ((Widget w));
  1159.  
  1160. static int
  1161. count_lines(w)
  1162. Widget w;
  1163. {
  1164.     int    nlines;
  1165.     XawTextPosition    pos, len;
  1166.  
  1167.     len = widget_text_length(w);
  1168.  
  1169.     nlines = 0;
  1170.     pos = 0;
  1171.     while ((pos = XawTextSourceScan(XawTextGetSource(w),
  1172.                     pos, XawstEOL, XawsdRight, 1, TRUE))
  1173.        != XawTextSearchError) {
  1174.     nlines++;
  1175.     if (pos >= len)
  1176.       break;
  1177.     }
  1178.     return nlines;
  1179. }
  1180.  
  1181. static void drop_lines_from_top PARAMS ((Widget w, int num));
  1182.  
  1183. static void
  1184. drop_lines_from_top(w, num)
  1185. Widget w;
  1186. int num;
  1187. {
  1188.     int    nlines;
  1189.     XawTextPosition pos, len;
  1190.     XawTextBlock tb;
  1191.     XawTextEditType et;
  1192.  
  1193.     len = widget_text_length(w);
  1194.  
  1195.     nlines = num;
  1196.     pos = 0;
  1197.     while (nlines &&
  1198.        (pos = XawTextSourceScan(XawTextGetSource(w),
  1199.                     pos, XawstEOL, XawsdRight, 1, TRUE))
  1200.        != XawTextSearchError) {
  1201.     nlines--;
  1202.     if (pos >= len)
  1203.       break;
  1204.     }
  1205.     
  1206.     if (pos == XawTextSearchError)
  1207.       return;
  1208.     
  1209.     tb.firstPos = 0;
  1210.     tb.length = 0;
  1211.     tb.ptr = NULL;
  1212.     tb.format = FMT8BIT;
  1213.  
  1214.     /* Can we write to it? */
  1215.     nargs = 0;
  1216.     XtSetArg(tmpargs[nargs], XtNeditType, &et);  nargs++;
  1217.     XtGetValues(w, tmpargs, nargs);
  1218.  
  1219.     if (et == XawtextRead) {
  1220.     /* Make it writable. */
  1221.     nargs = 0;
  1222.     XtSetArg(tmpargs[nargs], XtNeditType, XawtextEdit);  nargs++;
  1223.     XtSetValues(w, tmpargs, nargs);
  1224.     }
  1225.     
  1226.     XawTextReplace(w, (XawTextPosition) 0, pos, &tb);
  1227.  
  1228.     if (et == XawtextRead) {
  1229.     /* Set it back. */
  1230.     nargs = 0;
  1231.     XtSetArg(tmpargs[nargs], XtNeditType, et);  nargs++;
  1232.     XtSetValues(w, tmpargs, nargs);
  1233.     }
  1234.  
  1235.     move_caret_to_end(w);
  1236. }
  1237.  
  1238. void
  1239. #ifdef ANSI_PROTOTYPES
  1240. textw_printf(const Widget w, const char *fmt, ...)
  1241. #else
  1242. textw_printf(w, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  1243. Widget w;
  1244. char *fmt;
  1245. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  1246. #endif
  1247. {
  1248.     XawTextBlock tb;
  1249.     XawTextPosition ins_point;
  1250.     XawTextEditType et;
  1251.     int nlines;
  1252.     char buf[BUFSIZ];
  1253.  
  1254. #ifdef ANSI_PROTOTYPES
  1255.     {
  1256.     va_list vl;
  1257.  
  1258.     va_start(vl, fmt);
  1259.     (void) vsprintf(buf, fmt, vl);
  1260.     va_end(vl);
  1261.     }
  1262. #else
  1263.     sprintf(buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  1264. #endif
  1265.     if (w == (Widget) NULL) {
  1266.     printf("%s", buf);
  1267.     return;
  1268.     }
  1269.  
  1270.     tb.ptr = buf;
  1271.     tb.length = strlen(buf);
  1272.     tb.format = FMT8BIT;
  1273.     tb.firstPos = 0;
  1274.  
  1275.     /* Can we write to it? */
  1276.     nargs = 0;
  1277.     XtSetArg(tmpargs[nargs], XtNeditType, &et);  nargs++;
  1278.     XtGetValues(w, tmpargs, nargs);
  1279.  
  1280.     if (et == XawtextRead) {
  1281.     /* Make it writable. */
  1282.     nargs = 0;
  1283.     XtSetArg(tmpargs[nargs], XtNeditType, XawtextEdit);  nargs++;
  1284.     XtSetValues(w, tmpargs, nargs);
  1285.     }
  1286.     move_caret_to_end(w);
  1287.     ins_point = widget_text_length(w);
  1288.     XawTextReplace(w, ins_point, ins_point, &tb);
  1289.     move_caret_to_end(w);
  1290.     if (et == XawtextRead) {
  1291.     /* Set it back. */
  1292.     nargs = 0;
  1293.     XtSetArg(tmpargs[nargs], XtNeditType, et);  nargs++;
  1294.     XtSetValues(w, tmpargs, nargs);
  1295.     }
  1296.     nlines = count_lines(w);
  1297.     /* Need to make this a resource. */
  1298. #define maxlines 256
  1299.     if (nlines > maxlines)
  1300.       drop_lines_from_top(w, nlines - maxlines);
  1301. #undef maxlines
  1302. }
  1303.  
  1304. #if 0
  1305. void textw_iprintf PARAMS ((const Widget w, const char *fmt, ...));
  1306.  
  1307. void
  1308. #ifdef ANSI_PROTOTYPES
  1309. textw_iprintf(const Widget w, const char *fmt, ...)
  1310. #else
  1311. textw_iprintf(w, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  1312. Widget w;
  1313. char *fmt;
  1314. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  1315. #endif
  1316. {
  1317.     XawTextBlock tb;
  1318.     XawTextPosition    ins_point;
  1319.     XawTextEditType    et;
  1320.     char buf[BUFSIZ];
  1321.  
  1322. #ifdef ANSI_PROTOTYPES
  1323.     {
  1324.     va_list        vl;
  1325.  
  1326.     va_start(vl, fmt);
  1327.     (void) vsprintf(buf, fmt, vl);
  1328.     va_end(vl);
  1329.     }
  1330. #else
  1331.     sprintf(buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  1332. #endif
  1333.     tb.ptr = buf;
  1334.     tb.length = strlen(buf);
  1335.     tb.format = FMT8BIT;
  1336.     tb.firstPos = 0;
  1337.  
  1338.     /* Can we write to it? */
  1339.     nargs = 0;
  1340.     XtSetArg(tmpargs[nargs], XtNeditType, &et);  nargs++;
  1341.     XtGetValues(w, tmpargs, nargs);
  1342.  
  1343.     if (et == XawtextRead) {
  1344.     /* Make it writable. */
  1345.     nargs = 0;
  1346.     XtSetArg(tmpargs[nargs], XtNeditType, XawtextEdit);  nargs++;
  1347.     XtSetValues(w, tmpargs, nargs);
  1348.     }
  1349.     ins_point = XawTextGetInsertionPoint(w);
  1350.     XawTextReplace(w, ins_point, ins_point, &tb);
  1351.     if (et == XawtextRead) {
  1352.     /* Set it back. */
  1353.     nargs = 0;
  1354.     XtSetArg(tmpargs[nargs], XtNeditType, et);  nargs++;
  1355.     XtSetValues(w, tmpargs, nargs);
  1356.     }
  1357. }
  1358. #endif
  1359.  
  1360. #if 0
  1361. void
  1362. EraseTextWidget(w)
  1363. Widget w;
  1364. {
  1365.     XawTextBlock block;
  1366.  
  1367.     block.ptr = "";
  1368.     block.length = 0;
  1369.     block.firstPos = 0;
  1370.     block.format = FMT8BIT;
  1371.  
  1372.     XawTextReplace(w, 0, widget_text_length(w), &block);
  1373.     /* If this fails, too bad. */
  1374.     move_caret_to_end(w);
  1375. }
  1376. #endif
  1377.  
  1378. int
  1379. find_side_via_widget(w, sidep)
  1380. Widget w;
  1381. Side **sidep;
  1382. {
  1383.     Side *side;
  1384.  
  1385.     for_all_sides(side) {
  1386.     if (active_display(side)) {
  1387.         if (XtDisplay(w) == side->ui->dpy) {
  1388.         *sidep = side;
  1389.         return TRUE;
  1390.         }
  1391.     }
  1392.     }
  1393.     return FALSE;
  1394. }
  1395.  
  1396. void
  1397. low_send(id, buf)
  1398. int id;
  1399. char *buf;
  1400. {
  1401. }
  1402.  
  1403. int
  1404. low_receive(id, buf, maxchars, timeout)
  1405. int *id, maxchars, timeout;
  1406. char *buf;
  1407. {
  1408.   return 0;
  1409. }
  1410.